"""The application's model objects"""
from datetime import datetime
import hashlib
import logging
import os

import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.sql.expression import desc
from schemabot import SchemaBot, SchemaManager

import meta

# ==== ==== ==== ====
#import datetime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey, Text, DateTime, Float
from sqlalchemy.orm import relation, backref

# ==== ==== ==== ====

USERNAME_SIZE=16

log = logging.getLogger(__name__)

def init_model(engine, auto_schema_update=False):
    """Call me before using any of the tables or classes in the model"""
    ## Reflected tables must be defined and mapped here
    #global reflected_table
    #reflected_table = sa.Table("Reflected", meta.metadata, autoload=True,
    #                           autoload_with=engine)
    #orm.mapper(Reflected, reflected_table)
    #
    meta.Session.configure(bind=engine)
    meta.engine = engine

    schema_mgr = SchemaManager()
    schema_mgr.register(1, upgrade=schema_version_1_upgrade, downgrade=schema_version_1_downgrade)

    schemabot = SchemaBot(schema_mgr, engine=engine)
    (model_version, current_db_version) = schemabot.version_check()

    force_version = os.environ.get('SCHEMABOT_FORCE_VERSION')
    if force_version is not None:
        # User override of model_version. This will force an upgrade or downgrade
        #   to the specified model version.
        model_version = int(force_version)
        if model_version != 0 and schema_mgr.get(model_version) is None:
            raise ValueError("Model version %d requested by SCHEMABOT_FORCE_VERSION does not exist." %model_version)
        log.warn("Forcing a DB schema update to version %d" %model_version)

    if model_version != current_db_version:
        if auto_schema_update:
            schemabot.schema_update(model_version)

        else:
            raise Exception("SchemaBot reports that the model version (%d) does not match the DB schema version (%d). Please run 'paster setup-app config.ini' to update the database schema." %(model_version, current_db_version))

## Non-reflected tables may be defined and mapped at module level
user_table = sa.Table('user', meta.metadata,
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('user_name', sa.Unicode(USERNAME_SIZE), unique=True),
    sa.Column('email_address', sa.Unicode(255)),
    sa.Column('display_name', sa.Unicode(255)),
    sa.Column('password', sa.String(128)),          # room for SHA-512 hashed passwords (hexdigest)
    sa.Column('created', sa.DateTime, default=datetime.now),
    sa.Column('activated', sa.Boolean, nullable=False, default=False),
)
schema_version_1_upgrade = [user_table]
schema_version_1_downgrade = [user_table]
user_activation_table = sa.Table('user_activation', meta.metadata,
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('user_id', sa.Integer, sa.ForeignKey('user.id')),
    sa.Column('key', sa.Unicode(128), nullable=False),
    sa.Column('created', sa.DateTime, default=datetime.now),
)

schema_version_1_upgrade.append(user_activation_table)
schema_version_1_downgrade.append(user_activation_table)

# Insert a default user "xgo" with password "xgo"
default_user_password_hash = hashlib.sha512("xgo").hexdigest()
insert_default_user = 'INSERT INTO "user" '                             \
        "(id, user_name, display_name, password, activated) VALUES "    \
        "(0, 'xgo', 'Default User', '%s', True)"%default_user_password_hash
schema_version_1_upgrade.append(insert_default_user)

# ---- ORM Mapped Classes ----

class User(object):
    def __init__(self, **kwargs):
        for key,value in kwargs.items():
            if key == 'password':
                # Hash password
                self.set_password(value)
            else:
                setattr(self, key, value)

    def validate_password(self, password):
        """The given password is hashed and compared against the one
        stored in the database.  Returns True if they are equal, else
        False.

        This method is called by repoze.who.plugins.sa.SQLAlchemyAuthenticatorPlugin
        """
        hashed_password = self._hash_password(password)
        return self.password == hashed_password and self.activated

    def set_password(self, raw_pass):
        """Set a new password for the account.  The raw password
        will be stored in hashed form and will not be reversible.
        """
        self.password = self._hash_password(raw_pass)

    def _hash_password(self, raw_pass):
        return hashlib.sha512(raw_pass).hexdigest()


class UserActivation(object):
    pass

orm.mapper(User, user_table,
    properties = dict(
        activation = sa.orm.relation(UserActivation, backref='user', uselist=False, cascade="all, delete, delete-orphan"),
    )
)
orm.mapper(UserActivation, user_activation_table)


## Classes for reflected tables may be defined here, but the table and
## mapping itself must be done in the init_model function
#reflected_table = None
#
#class Reflected(object):
#    pass


#==== ==== ==== ==== ==== ==== ==== ======== ==== ==== ======== ==== ==== ======== ==== ==== ====
#==== ==== ==== ==== ==== ==== ==== ======== ==== ==== ======== ==== ==== ======== ==== ==== ====
#==== ==== ==== ==== ==== ==== ==== ======== ==== ==== ======== ==== ==== ======== ==== ==== ====


Base = declarative_base()
#==== ==== ==== ====
# CATEGORY
#==== ==== ==== ====
class Source(Base):
    __tablename__  = "source"

    source_id = Column(Integer, primary_key=True)
    name = Column(String(128))

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return "<Source('%s','%s')>" % (self.category_id, self.name)

#==== ==== ==== ====
# LANGUAGE
#==== ==== ==== ====
class Language(Base):
    __tablename__ = "language"

    language_id = Column(Integer, primary_key=True)
    name = Column(String(32))

###==== ==== ==== ====
### TAG
###==== ==== ==== ====
##class Tag(Base):
##    __tablename__  = "tag"
##
##    tag_id = Column(Integer, primary_key=True)
##    name = Column(String(64))
##
##    def __init__(self, name):
##        self.name = name
##
##    def __repr__(self):
##        return "<Tag('%s','%s')>" % (self.name)
##
###==== ==== ==== ====
### NEWS_TAGS
###==== ==== ==== ====
##news_tags = Table('news_tags', Base.metadata,
##    Column('news_id', Integer, ForeignKey('news.news_id')),
##    Column('tag_id', Integer, ForeignKey('tag.tag_id'))
##    )
##
###==== ==== ==== ====
### NEWS_WORDS
###==== ==== ==== ====
##news_words_table = Table('news_words', Base.metadata,
##    Column('news_id', Integer, ForeignKey('news.news_id'), primary_key=True),
##    Column('word_id', Integer, ForeignKey('word.word_id'), primary_key=True),
##    Column('count', Integer)
##    )
##
##class NewsWords(object):
##    pass
##
##
##
##
###==== ==== ==== ====
### NEWS
###==== ==== ==== ====
##class News(Base):
##    __tablename__  = "news"
##
##    news_id = Column(Integer, primary_key=True)
##    link = Column(String(256))
##    title = Column(String(256))
##    excerpt = Column(String(1000))
##    content = Column(Text)
##    content_normalized = Column(Text)
##    rank = Column(Integer)
##    category_id = Column(Integer, ForeignKey('category.category_id'))
##    language_id = Column(Integer, ForeignKey('language.language_id'))
##    date_created = Column(DateTime, default=datetime.now)
##    etl_id = Column(String(32))
##    class_id = Column(Integer)
##    comments = Column(Integer)
##    reads = Column(Integer)
##
##    category = relation(Category)
##    tags = relation('Tag', secondary=news_tags, backref='news')
##    words = relation(NewsWords, order_by=desc("count"))
##    #kernel_results = relation(KernelResults, order_by=KernelResults.similarity, backref="news")
##
##    def __init__(self, link, title, content, excerpt='' , rank=-1, category_id=None, language_id=None, etl_id='', class_id=1, comments=0, reads=0):
##        self.link = link
##        self.title = title
##        self.excerpt = excerpt
##        self.content = content
##        self.rank = rank
##        self.category_id = category_id
##        self.language_id = language_id
##        self.etl_id = etl_id
##        self.class_id = class_id
##        self.comments = comments
##        self.reads = reads
##
##    def __repr__(self):
##        return u"<News(c: %s - '%s','%s', '%s')>" % (self.class_id, self.title, self.link, self.content[:100])
##
##
###==== ==== ==== ====
### KERNEL_RESULTS
###==== ==== ==== ====
##class Kernel(Base):
##    __tablename__  = "kernel"
##
##    kernel_id = Column(Integer, primary_key=True)
##    kernel_name = Column(String(256))
##    kernel_description = Column(String(1024))
##
##    def __init__(self, kernel_name, kernel_description):
##        self.kernel_name = kernel_name
##        self.kernel_description = kernel_description
##
##    def __repr__(self):
##        return u"<Kernel('%s', '%s')>" % (self.kernel_name, self.kernel_description)
##
##
###==== ==== ==== ====
### KERNEL_RESULTS
###==== ==== ==== ====
##class KernelResults(Base):
##    __tablename__  = "kernel_results"
##
##    id_row = Column(Integer, primary_key=True)
##    news_id_1 = Column(Integer, ForeignKey('news.news_id'))
##    news_id_2 = Column(Integer) #, ForeignKey('news.news_id')
##    similarity = Column(Float(32))
##    kernel_id = Column(Integer, ForeignKey('kernel.kernel_id'))
##    param1 = Column(Float(32))
##    param2 = Column(Float(32))
##    param3 = Column(Float(32))
##    param4 = Column(Float(32))
##
##    news = relation(News, backref=backref('kernel_results', order_by=desc(similarity)))
##    kernel = relation(Kernel, backref=backref('kernel_results'))
##    #news2 = relation(News, backref=backref('kernel_results', order_by=desc(similarity)))
##
##    def __init__(self, news_id_1, news_id_2, similarity, kernel_id, param1=None, param2=None, param3=None, param4=None):
##        self.news_id_1 = news_id_1
##        self.news_id_2 = news_id_2
##        self.similarity = similarity
##        self.kernel_id = kernel_id
##        self.param1 = param1
##        self.param2 = param2
##        self.param3 = param3
##        self.param4 = param4
##
##    def __repr__(self):
##        return u"<KernelResults('%s', '%s', '%s', '%s')>" % (self.news_id_1, self.news_id_2, self.similarity, self.kernel_id)
##
###==== ==== ==== ====
### STOPWORD
###==== ==== ==== ====
##class StopWords(Base):
##    __tablename__  = "stop_words"
##
##    word_id = Column(Integer, primary_key=True)
##    word = Column(String(96))
##
##    def __init__(self, word):
##        self.word = word
##
##    def __repr__(self):
##        return u"<StopWord('%s')>" % (self.word)
##
##
###==== ==== ==== ====
### WORD
###==== ==== ==== ====
##class Word(Base):
##    __tablename__  = "word"
##
##    word_id = Column(Integer, primary_key=True)
##    word = Column(String(96))
##
##    def __init__(self, word):
##        self.word = word
##
##    def __repr__(self):
##        return u"<Word('%s')>" % (self.word)
##
##
##orm.mapper(NewsWords, news_words_table, properties={
##    'word':relation(Word)
##})





